package org.mozhu.mboot.admin.maintain.file.service;

import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.joda.time.LocalDate;
import org.mozhu.mboot.core.file.util.PathUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
import java.util.UUID;

@Service
public class FileService {
    private static final Logger LOGGER = LoggerFactory.getLogger(FileService.class);

    private static final String FILE_PREFIX = "file:";
    private static final String UPLOAD = "upload";
    private static final String TEMP = "temp";
    private String resourceBasePath;

    public void copyDirectory(String fromFilePath, String toFilePath) {
        File fromResourceFile = this.getResourceFile(fromFilePath);
        File toResourceFile = this.getResourceFile(toFilePath);
        try {
            LOGGER.info("copy directory from [{}] to [{}]", fromFilePath, toFilePath);
            if (fromResourceFile.exists()) {
                FileUtils.copyDirectory(fromResourceFile, toResourceFile);
            }
        } catch (IOException e) {
            LOGGER.error("copy directory failed", e);
        }
    }

    public String copyFileToDir(MultipartFile file, String dir, String fileName) {
        if (fileName == null) {
            fileName = file.getOriginalFilename();
        }
        String serverFilePath = PathUtils.join(dir, fileName);
        String path = PathUtils.getResourcePath(resourceBasePath, serverFilePath);
        copyFile(file, path);
        return serverFilePath;
    }

    public String copyFileToDir(MultipartFile file, String dir) {

        return copyFileToDir(file, dir, null);
    }

    public String copyFileToUploadDir(MultipartFile file, String dir) {
        String serverFilePath = PathUtils.join(dir, generateFileName(file.getOriginalFilename()));
        serverFilePath = wrapAsUploadDirectory(serverFilePath);
        String filePath = PathUtils.getResourcePath(resourceBasePath, serverFilePath);
        copyFile(file, filePath);
        return serverFilePath;
    }

    public void copyFile(MultipartFile file, String filePath) {
        try {
            FileUtils.copyInputStreamToFile(new ByteArrayInputStream(file.getBytes()), new File(filePath));
            LOGGER.info("Copy file[{}] to dir[{}]", file.getOriginalFilename(), filePath);
        } catch (IOException e) {
            LOGGER.error("Copy file[" + file.getOriginalFilename() + "] to dir[" + filePath + "] error", e);
        }
    }

    public String copyFileToTemp(MultipartFile file) {
        return copyFileToUploadDir(file, TEMP);
    }

    public List<String> processTempFiles(List<String> filePaths, String directory) {
        if (CollectionUtils.isEmpty(filePaths)) {
            return filePaths;
        }
        List<String> processedFilePaths = Lists.newArrayList();
        for (String filePath : filePaths) {
            processedFilePaths.add(processTempFile(filePath, directory));
        }
        return processedFilePaths;
    }

    public String processTempFile(String filePath, String directory) {
        if (!isTempFile(filePath)) {
            return filePath;
        }
        if (directory.endsWith(PathUtils.SEPARATOR)) {
            directory = directory + new LocalDate().toString();
        } else {
            directory = directory + PathUtils.SEPARATOR + new LocalDate().toString();
        }
        return moveFileToDirectory(filePath, directory);
    }

    public boolean isTempFile(String filePath) {
        return filePath.startsWith(TEMP) || filePath.startsWith(PathUtils.SEPARATOR + TEMP);
    }

    public boolean isUploadDirectory(String filePath) {
        return filePath.startsWith(UPLOAD) || filePath.startsWith(PathUtils.SEPARATOR + UPLOAD);
    }

    public String wrapAsUploadDirectory(String filePath) {
        if (isUploadDirectory(filePath)) {
            return filePath;
        }
        if (filePath.startsWith(PathUtils.SEPARATOR)) {
            return UPLOAD + filePath;
        }
        return PathUtils.join(UPLOAD, filePath);
    }

    public String moveFileToDirectory(String filePath, String directory) {
        try {
            File file = new File(getResourceAbsolutePath(filePath));
            directory = wrapAsUploadDirectory(directory);
            FileUtils.moveFileToDirectory(file, new File(getResourceAbsolutePath(directory)), true);
            return PathUtils.join(directory, file.getName());
        } catch (IOException e) {
            LOGGER.error("Copy file[" + filePath + "] to directory[" + directory + "] error", e);
        }
        return null;
    }

    public void deleteFiles(String[] filePaths) {
        if (ArrayUtils.isEmpty(filePaths)) {
            return;
        }
        for (String filePath : filePaths) {
            deleteFile(filePath);
        }
    }

    public void deleteFiles(List<String> filePaths) {
        if (CollectionUtils.isEmpty(filePaths)) {
            return;
        }
        for (String filePath : filePaths) {
            deleteFile(filePath);
        }
    }

    public void deleteFile(String filePath) {
        LOGGER.info("Delete filePath [{}]", filePath);
        if (filePath == null) {
            return;
        }
        FileUtils.deleteQuietly(new File(getResourceAbsolutePath(filePath)));
    }

    public String getResourceAbsolutePath(String filePath) {
        return PathUtils.getResourcePath(resourceBasePath, filePath);
    }

    public File getResourceFile(String filePath) {
        return new File(getResourceAbsolutePath(filePath));
    }

    private String generateFileName(String fileName) {
        String fileExtension = "";
        int lastIndexOf = fileName.lastIndexOf(".");
        if (lastIndexOf > 0) {
            fileExtension = fileName.substring(lastIndexOf);
        }
        return PathUtils.join(new LocalDate().toString(), UUID.randomUUID().toString().toUpperCase() + fileExtension);
    }

    public String getSuffix(String fileName) {
        return fileName.substring(fileName.lastIndexOf("."), fileName.length());
    }

    public String copyUrlToPath(String url, String path) {
        path = wrapAsUploadDirectory(path);
        try (InputStream inputStream = new URL(url).openStream()) {
            FileUtils.copyInputStreamToFile(inputStream, new File(getResourceAbsolutePath(path)));
        } catch (Exception e) {
            LOGGER.error("Copy url[" + url + "] to path[" + path + "] fail", e);
            throw new RuntimeException("Copy url[" + url + "] to path[" + path + "] fail", e);
        }
        return path;
    }

    @Value("${external.static.resource.location}")
    public void setResourceBasePath(String uploadBasePath) {
        if (uploadBasePath.startsWith(FILE_PREFIX)) {
            this.resourceBasePath = uploadBasePath.substring(FILE_PREFIX.length());
        } else {
            this.resourceBasePath = uploadBasePath;
        }
    }
}
